/*
 * LocationServer.cpp
 *
 *  Created on: 2013. 8. 5.
 *      Author: jjaehuny
 */

#include "LocationServer.h"
#include "Static.h"

#include <Network/Static.h>
#include <Logger/Static.h>

#include "../Proxy.h"
#include "../User.h"
#include "../Session.h"
#include "../Static.h"

#include "../NLogic/BackEndServerInfo.h"
#include "../NState/Static.h"
#include "../command/world/CS_CHAT_MESSAGE.h"

#include "Static.h"

namespace ProjectZ { namespace NProxy {

void
LocationServer::Msg::OnOpenAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	PROCESSLOG("[LOCATION] OnOpen Server IP : %s PORT : %d",
					pSession->GetNetworkPeerInfo().ip.c_str(), pSession->GetNetworkPeerInfo().port);

	Session * pLocalSession = static_cast<Session *>(pSession);
	LocationServer * pServer = static_cast<LocationServer *>(pLocalSession->GetOwner());
	::ProjectZ::Static::Instance()._locationServer[pServer->GetIdx()] = true;
}

void
LocationServer::Msg::OnCloseAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	PROCESSLOG("[LOCATION] OnClose Server IP : %s PORT : %d",
						pSession->GetNetworkPeerInfo().ip.c_str(), pSession->GetNetworkPeerInfo().port);

	Session * pLocalSession = static_cast<Session *>(pSession);
	LocationServer * pServer = static_cast<LocationServer *>(pLocalSession->GetOwner());
	pServer->OnDisconnect();
}

void
LocationServer::Msg::OnRegistAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::RegistAck ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		DEBUGLOG("[LOCATION OnRegistAck] ans.Load fail");
		return;
	}

	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		DEBUGLOG("[LOCATION OnRegistAck] Not Found User seq : %d", ans.seq);
		::ProjectZ::NProxy::UnRegistUser(ans.seq);
		return;
	}

	// 등록 실패... 유저 종료 처리 ....
	if (LOCATION_FAIL == ans.result)
	{
		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		DEBUGLOG("[LOCATION OnRegistAck] ACK_EXIST_USER");
		pAck->Write2(ACK_EXIST_USER);
		* pAck << 0;
		pAck->MakePacket(CMD_SC_REQ_LOGIN);
		pUser->Send(pAck);

		pUser->SetState(NState::Static::Instance().TITLE());
		NProxy::RemoveUser(ans.seq);

//		auto itr = s_UserTree.find(ans.seq);
//		if (itr != s_UserTree.end())
//		{
//			DEBUGLOG("[LOCATION OnRegistAck] FOUND USER SEQ : %d", ans.seq);
//			s_UserTree.erase(itr);
//		}
//		else if(itr == s_UserTree.end())
//		{
//			DEBUGLOG("[LOCATION OnRegistAck] NOT FOUND USER SEQ : %d", ans.seq);
//		}
		return;
	}



	// 로그인 성공 메시지 전송.... ?

	// 유저 정보를 요청 하자...
	DEBUGLOG("[LOCATION OnRegistAck ] Regist Success ProjectZ::UserInfoSyn");
	::ProjectZ::NProxy::UserInfoSyn(pUser);
	pUser->SetLocationRegist();

	pUser->SetState(NState::Static::Instance().READYMAINFRIENDLIST());
}

void
LocationServer::Msg::OnUnRegistAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::UnRegistAck ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		DEBUGLOG("[LOCATION OnUnRegistAck] false == ans.Load(&pBuf, len)");
		return;
	}

	// 이 경우는 unregist 를 다시 호출 해야 하는건가???
	if (LOCATION_FAIL == ans.result)
	{
		DEBUGLOG("[LOCATION OnUnRegistAck] LOCATION_FAIL == ans.result");

		return;
	}

	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		DEBUGLOG("[LOCATION OnUnRegistAck] NULL == pUser");
		return;
	}

	if (pUser->GetState() == NState::Static::Instance().PHYSICALCHANGE())
	{
		::ProjectZ::NProxy::Final(pUser);

		::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
		pAck->MakePacket(CMD_SC_RES_PHYSICAL_CHANGE_CLEANUP_COMPLETE);
		pUser->Send(pAck);
	}

	//::ProjectZ::NProxy::Final(pUser);
//	if (true == pUser->GetDelUser())
//		pUser->DecreaseRefCount();
//		User::Factory::Instance().DEL(pUser);
}

void
LocationServer::Msg::OnChatSyn(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::ChatSyn ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;
	if(false == ans.Load(&pBuf, len))
	{
		return;
	}
	// 받을 유저 즉 receiver_seq 로 유저를 검색하여 전달해야 한다.
	User * pTargetUser = GetUser(ans.receiver_seq);
	if (NULL == pTargetUser)
	{
		return;
	}

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	* pAck << static_cast<int16_t>(ACK_OK) << static_cast<uint8_t>(ans.type);
	* pAck << ans.sender_nick.c_str() << pTargetUser->GetUserInfo().nickname.c_str() << ans.msgs.c_str() << ans.sender_seq << ans.receiver_seq;

	pAck->MakePacket(CMD_SN_CHAT_MESSAGE);
	pTargetUser->Send(pAck);
}

void
LocationServer::Msg::OnChatAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	DEBUGLOG("LocationServer %s", __FUNCTION__);


	::ChatAck ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	// 이 경우는 Chat 를 요청했으나 실패가 발생한 경우 이다.
//	LocationResult value = ans.result;

	User * pTargetUser = GetUser(ans.receiver_seq);
	if (NULL == pTargetUser)
		return;

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	if( ans.type == ProjectZ::NCommand::NWorld::CHATTYPE_WHISPER )
		* pAck << static_cast<int16_t>(ACK_NOT_EXIST_USER);
	else
		* pAck << static_cast<int16_t>(ACK_UNKNOWN_ERROR);

	* pAck << static_cast<uint8_t>(ans.type) << pTargetUser->GetUserInfo().nickname.c_str()
			<< static_cast<uint16_t>(0) << static_cast<uint16_t>(0) << ans.sender_seq << ans.receiver_seq;

	pAck->MakePacket(CMD_SN_CHAT_MESSAGE);
	pTargetUser->Send(pAck);
}

void
LocationServer::Msg::OnRecallSyn(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::RecallSyn ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	// 받을 유저 즉 receiver_seq 로 유저를 검색하여 전달해야 한다.
	int receiver_seq = ans.receiver_seq;
	User * pTargetUser = ::ProjectZ::NProxy::GetUser(receiver_seq);
	if (NULL == pTargetUser)
	{
		ProjectZ::NProxy::RecallAck(receiver_seq, ans.sender_seq, 2);
		return;
	}
	else
	{
		NLogic::Space * space = pTargetUser->GetSpace();
		if(NULL == space)
		{
			ProjectZ::NProxy::RecallAck(receiver_seq, ans.sender_seq, 2);
			return;
		}
		else if(User::EnumState::__NONE__ != pTargetUser->GetEnumState() && NLogic::Space::SpaceType::WORLD != space->GetData()._spaceType)
		{
			ProjectZ::NProxy::RecallAck(receiver_seq, ans.sender_seq, 2);
			return;
		}
	}

	//int sender_seq = ans.sender_seq;
	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	*pAck << ans.sender_seq
			<< inet_addr(ans.ip.c_str())
			<< static_cast<uint16_t>(ans.port)
			<< static_cast<uint16_t>(ans.server_id)
			<< static_cast<uint16_t>(ans.channel_id);

	pAck->MakePacket(CMD_SC_SOCIAL_SUMMON_FROM_FRIEND);
	pTargetUser->Send(pAck);
}

void
LocationServer::Msg::OnRecallAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::RecallAck ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	int receiver_seq = ans.receiver_seq;
	User * pTargetUser = ::ProjectZ::NProxy::GetUser(receiver_seq);
	if (NULL == pTargetUser)
	{
		return;
	}
	// 이 경우는 Recall 를 요청했으나 실패가 발생한 경우 이다.
	//LocationResult value = ans.result;
	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	*pAck << static_cast<int16_t>(ACK_OK) << ans.receiver_seq << static_cast<uint8_t>(ans.is_accept);

	pAck->MakePacket(CMD_SC_SOCIAL_SUMMON_FRIEND);
	pTargetUser->Send(pAck);
}

void
LocationServer::Msg::OnKickUser(Dispatcher::Session* pSession, ::Network::Packet * pPacket )
{
	::KickUser ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		DEBUGLOG("OnKickUser false == ans.Load(&pBuf, len)");
		return;
	}

	// 유저가 중복 접속한 경우 먼저 접속된 유저이다.
	// 접속 종료 처리를 해야한다.
	// 메시지 전달 필요???
	//int seq = ans.seq;
	User * pTargetUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pTargetUser)
	{
		DEBUGLOG("OnKickUser NULL == pTargetUser USER SEQ : %d", ans.seq);

		::ProjectZ::NProxy::UnRegistUser(ans.seq);
		return;
	}

	if (NULL == pTargetUser->GetSession())
	{
		DEBUGLOG("OnKickUser NULL == pTargetUser->GetSession()");
		pTargetUser->SetKick();
		//::ProjectZ::NProxy::UnRegistUser(ans.seq);
		return;
	}

	DEBUGLOG("OnKickUser");

	//pTargetUser->SetClose();
	//pTargetUser->GetSession()->Close();

	::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
	* pNoti << static_cast<uint8_t>(1);
	pNoti->MakePacket(CMD_SN_SERVER_MESSAGE);

	pTargetUser->Send(pNoti);

	//pTargetUser->OnDisconnect();

	User::State * pState = pTargetUser->GetState();
	if (pState != NULL)
	{
		//pState->OnClose(pTargetUser, false);
		pState->OnClose(pTargetUser, true);
	}
}


void
LocationServer::Msg::OnTradeItemSoldoutSyn(Dispatcher::Session* pSession, ::Network::Packet * pPacket)
{
	::TradeItemSoldoutSyn ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		DEBUGLOG("OnTradeItemSoldoutSyn false == ans.Load(&pBuf, len)");
		return;
	}

	User * pTargetUser = ::ProjectZ::NProxy::GetUser(ans.seller_userseq);
	if (NULL == pTargetUser)
	{
		DEBUGLOG("OnTradeItemSoldoutSyn : pTargetUser is NULL!! seq=%d", ans.seller_userseq);
		return;
	}

	NLogic::Trade * pTrade = pTargetUser->GetTrade();
	if (NULL == pTrade)
	{
		DEBUGLOG("OnTradeItemSoldoutSyn : pTrade is NULL!! seq=%d", ans.seller_userseq);
		return;
	}

	NLogic::Trade::Key clsKey;
	clsKey.tradeSeq		= ans.item_trade_seq;
	clsKey.classType	= ans.item_class_type;
	clsKey.sheetType	= ans.item_sheet_type;

	NLogic::Trade::TradeItem * pTradeItem = pTrade->GetTradeItemData(clsKey);
	if (NULL == pTradeItem)
	{
		DEBUGLOG("OnTradeItemSoldoutSyn : pTradeItem is NULL!! tradeseq=%d, class=%d, sheet=%d",
				ans.item_trade_seq, ans.item_class_type, ans.item_sheet_type);
		return;
	}

	NLogic::Trade::SoldItem clsSoldItem;
	clsSoldItem.classType		= pTradeItem->GetData().item.class_type;
	clsSoldItem.curRefineStep	= pTradeItem->GetData().item.cur_refine_step;
	clsSoldItem.itemPrice		= pTradeItem->GetData().item_price;
	clsSoldItem.itemQuality		= pTradeItem->GetData().item.quality;
	clsSoldItem.seq				= pTradeItem->GetTradeSelfSeq();
	clsSoldItem.sheetType		= pTradeItem->GetData().item.sub_type;
	clsSoldItem.soldTime		= time(NULL);
	clsSoldItem.tid				= pTradeItem->GetData().item.tid;

	// 판매목록에 추가
	NLogic::Trade::SOLDITEMVECTOR & clsVector = pTrade->GetSoldItemVector();
	clsVector.push_back(clsSoldItem);

	// 내목록에서 삭제
	pTrade->RemoveTradeItemData(clsKey);
}

void
LocationServer::Msg::OnSetMainCharacterInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket)
{
	::SetMainCharacterInfoAck ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		return;
	}

	DEBUGLOG("%s : result[%d]", __FUNCTION__, ans.result);
}

void
LocationServer::Msg::OnGetMainCharacterInfoAck(Dispatcher::Session* pSession, ::Network::Packet * pPacket)
{
	::GetMainCharacterInfoAck ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		return;
	}

	int16_t result = ACK_OK;
	if(ACK_OK != ans.result)
		result = ACK_EXIST_USER;

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	*pAck << result
			<< ans.target_seq
			<< ans.level
			<< static_cast<uint8_t>(ans.class_type)
			<< static_cast<uint8_t>(ans.head)
			<< static_cast<uint8_t>(ans.armor)
			<< static_cast<uint8_t>(ans.weapon)
			<< static_cast<uint8_t>(ans.side_hand)
			<< static_cast<uint8_t>(ans.effect)
			<< ans.pet_tid;

	pAck->MakePacket(CMD_SC_SOCIAL_CHARACTER_INFO);
	pUser->Send(pAck);
}

void
LocationServer::Msg::OnGetMainCharacterInfoAck_V2(Dispatcher::Session* pSession, ::Network::Packet * pPacket)
{
	::GetMainCharacterInfoAck_V2 ans;

	const char* pBuf = pPacket->GetOffsetPtr();
	size_t len = pPacket->m_nEnd - pPacket->m_nOffset;

	if(false == ans.Load(&pBuf, len))
	{
		return;
	}

	User * pUser = ::ProjectZ::NProxy::GetUser(ans.seq);
	if (NULL == pUser)
	{
		return;
	}

	int16_t result = ACK_OK;
	if(ACK_OK != ans.result)
		result = ACK_NOT_EXIST_USER;

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	*pAck << result
			<< ans.target_seq
			<< ans.level
			<< static_cast<uint8_t>(ans.class_type)
			<< static_cast<uint8_t>(ans.head)
			<< static_cast<uint8_t>(ans.armor)
			<< static_cast<uint8_t>(ans.weapon)
			<< static_cast<uint8_t>(ans.side_hand)
			<< static_cast<uint8_t>(ans.effect)
			<< ans.pet_tid
			<< static_cast<uint8_t>(ans.profile_opened)
			<< static_cast<uint8_t>(ans.gender)
			<< static_cast<uint8_t>(ans.gender_open)
			<< ans.strBirthday.c_str()
			<< static_cast<uint8_t>(ans.birthday_open);

	// TODO 신규 내용을 추가하자.

	pAck->MakePacket(CMD_SC_SOCIAL_CHARACTER_INFO_V2);
	pUser->Send(pAck);
}

LocationServer::LocationServer() : _pSession(NULL), _bConnect(false), _idx(0)
{
	//_pSession = new ::Dispatcher::Session;
	_pSession = new Session;
	_pSession->SetState(new State());

	_pSession->SetOwner(this);
}

LocationServer::~LocationServer() {
	delete _pSession->GetState();
	delete _pSession;
}


bool
LocationServer::Connect(int idx, std::string strIP, int port)
{
	//::Network::Session::Info clsInfo;
	_clsInfo.eType = ::Network::Session::Info::SERVER;
	_clsInfo.ip = strIP.c_str();
	_clsInfo.port = port;
	_clsInfo.keepAliveTime = 3000 * 10;
	_idx = idx;

	bool bConnect = _pSession->Connect(_clsInfo);

	if (true == bConnect)
		_bConnect = true;

	return bConnect;
}

void
LocationServer::OnDisconnect()
{
	auto tree = GetUserTree();
	auto itr = tree.begin();
	for ( ; itr != tree.end(); ++itr)
	{
		User * pUser = itr->second;
		if (NULL == pUser)
			continue;

		::ProjectZ::Session * pSession = pUser->GetSession();
		if (NULL == pSession)
			continue;

		NLogic::BackEndServerInfo * pBackEndServerInfo = pUser->GetBackEndServerInfo();
		if (NULL == pBackEndServerInfo)
			continue;

		if (pBackEndServerInfo->GetData()._locationServerIndex != this->_idx)
			continue;

		//pSession->Close();
		User::State * pState = pUser->GetState();
		if (NULL != pState)
			pState->OnClose(pUser, true);

		//pUser->CleanUp();
	}

	_bConnect = false;
	this->setTime();
}

bool
LocationServer::OnTime(int id, void* param)
{
	_pSession = new Session;
	_pSession->SetState(new State());
	_pSession->SetOwner(this);

	bool bConnect = _pSession->Connect(_clsInfo);

	if (true == bConnect)
		_bConnect = true;
	else
	{
		// 타이머 재 등록...
		this->setTime();
	}

	return false;
}

void
LocationServer::setTime()
{
	// 15 초 타이머 진행....
	::Dispatcher::Timer::Argument clsArgument(1000 * 15, 0, NULL, this);
	::Dispatcher::GETTIMER()->SetTimer(clsArgument);
}

} /*NProxy*/ } /*ProjectZ*/

